#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define COMMAND_SIZE 1024
#define FORMATE "[%s@%s %s]# "
#define MAXRET 128
char* g_argv[MAXRET];
int g_argc=0;
int lastcode=0;
char cwd[1024];
char cwdenv[1024];
#define MAXENV 100
char* g_env[MAXENV];
int g_envs=0;
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
std::string filename;
int redir=NONE_REDIR;
const char* getusername()
{
  const char* name=getenv("USER");
  return name==NULL?"None":name;
}
const char* gethostname()
{
  const char* hostname=getenv("HOSTNAME");
  return hostname==NULL?"None":hostname;
}
const char* getpwd()
{
  //constpwd char* pwd=getenv("PWD");
  const char* pwd=getcwd(cwd,sizeof(cwd));
  if(pwd!=NULL)
  {
    snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
    putenv(cwdenv);
  }
  return pwd==NULL?"None":pwd;
}
const char* gethome()
{
  const char* home=getenv("HOME");
  return home==NULL?"":home;
}
std::string dirname(const char* mypwd)
{
  std::string ad=mypwd;
#define FG "/"
  auto pos=ad.rfind(FG);
  if(pos==std::string::npos)
  {
    return "BUG";
  }
  std::string nm=ad.substr(pos+1);
  return nm;
}
void makecommandline(char cmdline[],int size)
{
  snprintf(cmdline,size,FORMATE,getusername(),gethostname(),dirname(getpwd()).c_str());
}
void printcommandline()
{
  char prompt[COMMAND_SIZE];
  makecommandline(prompt,sizeof(prompt));
  printf("%s",prompt);
  fflush(stdout);
}
bool getcommand(char* out,int size)
{
  char *c=fgets(out,size,stdin);
  if(c==NULL)
  {
    return false;
  }
  out[strlen(out)-1]=0;
  if(strlen(out)==0)
  {
    return false;
  }
  return true;
}
bool commandparse(char* commandline)
{
#define SEP " "
  g_argc=0;
  g_argv[g_argc++]=strtok(commandline,SEP);
  while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
  g_argc--;
  return true;
}
bool Cd()
{
  if(g_argc==1)
  {
    std::string s=gethome();
    if(s.empty())
    {
      return true;
    }
    chdir(s.c_str());
  }
  else 
  {
    std::string where=g_argv[1];
    chdir(where.c_str());
  }
  return true;
}
bool Echo()
{
  if(g_argc==2)
  {
    std::string opt=g_argv[1];
    if(opt=="$?")
    {
      std::cout<<lastcode<<std::endl;
    }
    else if(opt[0]=='$') 
    {
      std::string s=opt.substr(1);
      const char* as=getenv(s.c_str());
      if(as)
        std::cout<<as<<std::endl;
    }
    else 
    {
      std::cout<<opt<<std::endl;
    }
  }
}
bool checkmingling()
{
  std::string s=g_argv[0];
  if(s=="cd")
  {
    Cd();
    return true;
  }
  else if(s=="echo")
  {
    Echo();
    return true;
  }
  return false;
}
int Execute()
{
  pid_t i=fork();
  if(i==0)
  {
    int fd=-1;
    if(redir==INPUT_REDIR)
    {
      fd=open(filename.c_str(),O_RDONLY);
      if(fd<0)
      {
        exit(1);
      }
      dup2(fd,0);
      close(fd);
    }

    else if(redir==OUTPUT_REDIR)
    {
      fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
      if(fd<0)
      {
        exit(2);
      }
      dup2(fd,1);
      close(fd);
    }
    else if(redir==APPEND_REDIR)
    {
      fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
      if(fd<0)
      {
        exit(2);
      }
      dup2(fd,1);
      close(fd);
    }
    execvp(g_argv[0],g_argv);
    exit(1);
  }
  int status=0;
  pid_t as=waitpid(i,&status,0);
  if(as>0)
  {
    lastcode=WEXITSTATUS(status);
  }
  return 0;
}
void init_env()
{
  extern char** environ;
  memset(g_env,0,sizeof(g_env));
  g_envs=0;
  for(int i=0;environ[i];i++)
  {
    g_env[i]=(char*)malloc(strlen(environ[i])+1);
    strcpy(g_env[i],environ[i]);
    g_envs++;
  }
  g_env[g_envs++]=(char*)"HAHA=test";
  g_env[g_envs]=NULL;
  for(int i=0;g_env[i];i++)
  {
    putenv(g_env[i]);
  }
  environ=g_env;
}
void trimspace(char cmd[],int& end)
{
  while(isspace(cmd[end]))
  {
    end++;
  }
}
void redircheck(char cmd[])
{
  redir=NONE_REDIR;
  filename.clear();
  int start=0;
  int end=strlen(cmd)-1;
  while(end>start)
  {
    if(cmd[end]=='<')
    {
      cmd[end++]=0;
      trimspace(cmd,end);
      redir=INPUT_REDIR;
      filename=cmd+end;
      break;
    }
    else if(cmd[end]=='>')
    {
      if(cmd[end-1]=='>')
      {
        cmd[end-1]=0;
        redir=APPEND_REDIR;
      }
      else{
        redir=OUTPUT_REDIR;
      }
      cmd[end++]=0;
      trimspace(cmd,end);
      filename=cmd+end;
    }
    else 
    {
      end--;
    }
  }


}
int main()
{
  //初始化环境变量
  init_env();
  while(true)
  {
  //1.输出命令行
  printcommandline();
  //2.获取用户输入命令
     char commandline[COMMAND_SIZE];
    if(!getcommand(commandline,sizeof(commandline)))
    {
      continue;
    }
    //3.重定向分析
    redircheck(commandline);
    //4.命令行分析
    commandparse(commandline);
    //5.检测是否为内键命令并执行
    if(checkmingling())
    {
      continue;
    }
    //6.执行命令
    Execute();
  }
  return 0;
}
